home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr46 / vfwdk.zip / VFWSDK.ZIP / SAMPLES / WRITEAVI / AVIEASY.C next >
C/C++ Source or Header  |  1993-01-31  |  21KB  |  728 lines

  1. /****************************************************************************
  2.  *
  3.  *  AVIEASY.C
  4.  *
  5.  *  low-level routines for writing Standard AVI files
  6.  *
  7.  *      AVIPhys...()
  8.  *
  9.  *  Copyright (c) 1992-1993 Microsoft Corporation.  All Rights Reserved.
  10.  *
  11.  *  You have a royalty-free right to use, modify, reproduce and
  12.  *  distribute the Sample Files (and/or any modified version) in
  13.  *  any way you find useful, provided that you agree that
  14.  *  Microsoft has no warranty obligations or liability for any
  15.  *  Sample Application Files which are modified.
  16.  *
  17.  ***************************************************************************/
  18.  
  19. #include <windows.h>
  20. #include <windowsx.h>
  21. #include <mmsystem.h>
  22. #include <compman.h>
  23. #include <memory.h>
  24. #include "avifmt.h"
  25. #include "avieasy.h"
  26.  
  27. // C6 needs a little help....
  28. #undef  GlobalFreePtr
  29. #define GlobalFreePtr(lp)     (BOOL)GlobalFree(GlobalPtrHandle(lp))
  30.  
  31. extern LONG FAR PASCAL muldiv32(LONG,LONG,LONG);
  32.  
  33. #define MAXSTREAMS    16
  34. typedef struct {
  35.     HMMIO            hmmio;
  36.     DWORD            dwStart;
  37.     int                iMaxStream;
  38.     LPVOID            lpFormat[MAXSTREAMS];
  39.     DWORD            cbFormat[MAXSTREAMS];
  40.     AVIStreamHeader        strhdr[MAXSTREAMS];
  41.     AVIINDEXENTRY huge *    hpIndex;
  42.     DWORD            dwIndex;
  43.     DWORD            dwIndexAlloc;
  44.     DWORD            dwIndexRec;
  45.     MMCKINFO            ckRECORD;
  46.     MainAVIHeader        avihdr;
  47. } PHYSINFO, FAR *PPHYS;
  48.  
  49. /* Some simple functions for dealing with AVI indices. */
  50. static BOOL NEAR PASCAL InitIndex(PPHYS pphys);
  51. static BOOL NEAR PASCAL AddChunkToIndex(PPHYS pphys, MMCKINFO FAR * lpck, DWORD dwFlags);
  52. static BOOL NEAR PASCAL WriteIndex(PPHYS pphys);
  53.  
  54.  
  55. ///////////////////////////////////////////////////////////////////////////
  56. ///////////////////////////////////////////////////////////////////////////
  57.  
  58.  
  59. /*****************************************************************************
  60.  * @doc EXTERNAL 
  61.  * 
  62.  * @api LONG | avifileOpen | Open an AVI file for writing.
  63.  * 
  64.  * @parm HAVI FAR * | lphfile | Holds the returned file handle if the
  65.  *    function succeeds.
  66.  * 
  67.  * @parm LPSTR | lpFileName | The file name to use.
  68.  *
  69.  * @parm MainAVIHeader FAR * | lphdr | The main AVI header to write to the file.
  70.  *
  71.  * @rdesc Returns AVIERR_OK if successful, an error code otherwise.
  72.  *
  73.  * @xref avifileClose
  74.  ****************************************************************************/
  75. LONG FAR avifileOpen(HAVI FAR *lphfile, LPSTR lpFileName, MainAVIHeader FAR *lphdr)
  76. {
  77.     PPHYS    pwrite;
  78.     LONG    lRet = AVIERR_OK;
  79.     
  80.     pwrite = (PPHYS) GlobalAllocPtr(GHND, sizeof(PHYSINFO));
  81.     if (!pwrite)
  82.     return AVIERR_MEMORY;
  83.  
  84.     pwrite->hmmio = mmioOpen(lpFileName, NULL, MMIO_WRITE | MMIO_CREATE);
  85.  
  86.     pwrite->avihdr = *lphdr;
  87.  
  88.     pwrite->avihdr.dwStreams = 0; // we'll use the # of streams they add
  89.     
  90.     if (!pwrite->hmmio)
  91.     goto OpenError;
  92.  
  93.     if (!InitIndex(pwrite))
  94.     goto FileError;
  95.  
  96.     *lphfile = (HAVI) pwrite;
  97.  
  98.     goto exit;
  99.     
  100. FileError:
  101.     lRet = AVIERR_FILEWRITE;
  102.     mmioClose(pwrite->hmmio, 0);
  103.     goto exit;
  104.     
  105. OpenError:
  106.     lRet = AVIERR_FILEOPEN;
  107.     
  108. exit:
  109.     return lRet;
  110.  
  111. }
  112.  
  113. ///////////////////////////////////////////////////////////////////////////
  114. ///////////////////////////////////////////////////////////////////////////
  115.  
  116. /*****************************************************************************
  117.  * @doc EXTERNAL 
  118.  * 
  119.  * @api LONG | avifileAddStream | Add a new stream to an AVI file.
  120.  * 
  121.  * @parm HAVI | hfile | The handle returned from <f avifileOpen>.
  122.  * 
  123.  * @parm int FAR * | lpstream | A pointer to an integer that will receive
  124.  *    the number of the new stream.  Can be NULL.
  125.  *
  126.  * @parm AVIStreamHeader FAR * | lphdr | The stream's header information.
  127.  *
  128.  * @parm LPVOID | lpFormat | A pointer to the stream's format, such as a
  129.  *    <t BITMAPINFO> or <t WAVEFORMAT> structure.
  130.  *
  131.  * @parm LONG | cbFormat | The size of the format pointed to be <p lpFormat>.
  132.  *
  133.  * @rdesc Returns AVIERR_OK if successful, an error code otherwise.
  134.  *
  135.  * @xref avifileOpen avifileWriteToHeader
  136.  ****************************************************************************/
  137. LONG FAR avifileAddStream(HAVI hfile, int FAR *lpstream,
  138.          AVIStreamHeader FAR *lphdr,
  139.          LPVOID lpFormat,
  140.          LONG   cbFormat)
  141. {
  142.     PPHYS pwrite = (PPHYS) hfile;
  143.     int    stream = (int) pwrite->avihdr.dwStreams++;
  144.  
  145.     if (lpstream)
  146.     *lpstream = stream;
  147.     
  148.     pwrite->cbFormat[stream] = cbFormat;
  149.     pwrite->lpFormat[stream] = GlobalAllocPtr(GMEM_MOVEABLE, cbFormat);
  150.     // !!!
  151.     hmemcpy(pwrite->lpFormat[stream], lpFormat, cbFormat);
  152.     pwrite->strhdr[stream] = *lphdr;
  153.     pwrite->strhdr[stream].dwLength = 0;
  154.  
  155.     // Fix up values in header...
  156.     if (lphdr->fccType == streamtypeAUDIO) {
  157.     LPWAVEFORMAT lpwf = (LPWAVEFORMAT) lpFormat;
  158.     pwrite->strhdr[stream].dwSampleSize = lpwf->nBlockAlign;
  159.     pwrite->strhdr[stream].dwRate = lpwf->nSamplesPerSec;
  160.     pwrite->strhdr[stream].dwScale = lpwf->nBlockAlign;
  161.     } else {
  162.     pwrite->strhdr[stream].dwSampleSize = 0;
  163.  
  164.     // Default to 15/sec....
  165.     if (pwrite->strhdr[stream].dwRate == 0 ||
  166.             pwrite->strhdr[stream].dwScale == 0) {
  167.         pwrite->strhdr[stream].dwRate = 15;
  168.         pwrite->strhdr[stream].dwScale = 1;
  169.     }
  170.     }
  171.  
  172.     if (lphdr->fccType == streamtypeVIDEO) {
  173.     LPBITMAPINFOHEADER  lpbi = (LPBITMAPINFOHEADER) lpFormat;
  174.  
  175.     if (pwrite->avihdr.dwWidth < (DWORD) lpbi->biWidth)
  176.         pwrite->avihdr.dwWidth = lpbi->biWidth;
  177.         
  178.     if (pwrite->avihdr.dwHeight < (DWORD) lpbi->biHeight)
  179.         pwrite->avihdr.dwHeight = lpbi->biHeight;        
  180.     }
  181.     
  182.     return AVIERR_OK;
  183. }
  184.  
  185. ///////////////////////////////////////////////////////////////////////////
  186. ///////////////////////////////////////////////////////////////////////////
  187.  
  188. /*****************************************************************************
  189.  * @doc EXTERNAL 
  190.  * 
  191.  * @api LONG | avifileWriteToHeader | Add data to the header of an AVI file.
  192.  * 
  193.  * @parm HAVI | hfile | The handle returned from <f avifileOpen>.
  194.  * 
  195.  * @parm int | stream | The stream to write to.  Can be -1, which indicates
  196.  *    that the data should be associated with the file as a whole, rather
  197.  *    than a single stream.
  198.  *
  199.  * @parm DWORD | ckid | The RIFF ckid to use for the data.
  200.  *
  201.  * @parm LPVOID | lpData | A pointer to the data to be written
  202.  *
  203.  * @parm LONG | cbData | The size of the data pointed to be <p lpData>.
  204.  *
  205.  * @rdesc Returns AVIERR_OK if successful, an error code otherwise.
  206.  *
  207.  * @xref avifileAddStream
  208.  ****************************************************************************/
  209. LONG FAR avifileWriteToHeader(HAVI    hfile,
  210.               int    stream,
  211.               DWORD    ckid,
  212.               LPVOID    lpData,
  213.               LONG    cbData)
  214. {
  215.     // !!! Add this data onto a list kept for each stream, so we can write
  216.     // it later.
  217.  
  218.     // stream -1 means main header?
  219.     
  220.     return AVIERR_OK;
  221. }
  222.  
  223. ///////////////////////////////////////////////////////////////////////////
  224. ///////////////////////////////////////////////////////////////////////////
  225.  
  226. /*****************************************************************************
  227.  * @doc EXTERNAL 
  228.  * 
  229.  * @api LONG | avifileWrite | Write data to an AVI file.
  230.  * 
  231.  * @parm HAVI | hfile | The handle returned from <f avifileOpen>.
  232.  * 
  233.  * @parm int | stream | The stream to write to.
  234.  *
  235.  * @parm LPVOID | lpData | A pointer to the data to be written
  236.  *
  237.  * @parm LONG | cbData | The size of the data pointed to be <p lpData>.
  238.  *
  239.  * @parm WORD | cktype | The TWOCC to use for the data.  Mostly obsolete,
  240.  *    except for RGB and RLE DIB data.
  241.  *
  242.  * @parm DWORD | dwFlags | Flags associated with this data.  In particular:
  243.  *
  244.  * @flag AVIIF_KEYFRAME | This data represents a key frame.
  245.  *
  246.  * @flag AVIIF_NOTIME | This data is control data, and does not take up
  247.  *    a frame's worth of time.
  248.  *
  249.  * @rdesc Returns AVIERR_OK if successful, an error code otherwise.
  250.  *
  251.  * @xref avifileAddStream
  252.  ****************************************************************************/
  253. LONG FAR avifileWrite(HAVI    hfile,
  254.               int    stream,
  255.               LPVOID    lpData,
  256.               LONG    cbData,
  257.               WORD    cktype,
  258.               DWORD    dwFlags)
  259. {
  260.     PPHYS pwrite = (PPHYS) hfile;
  261.     MMCKINFO    ck;
  262.     DWORD    dw;
  263.     int        i;
  264.  
  265.     if (stream >= (int) pwrite->avihdr.dwStreams)
  266.     return AVIERR_BADPARAM;
  267.  
  268.     if (cktype < 256 * ' ')
  269.     cktype = aviTWOCC('x', 'x');
  270.  
  271.     if (pwrite->dwStart == 0) {
  272.     // Figure out where to start writing the data
  273.     dw = 0;
  274.     for (i = 0; i < (int) pwrite->avihdr.dwStreams; i++) {
  275.         dw += pwrite->cbFormat[i] + 512;
  276.     }
  277.     // Reserve some space for the header
  278.     pwrite->dwStart = max(4096L, dw);
  279.     
  280.     mmioSeek(pwrite->hmmio, pwrite->dwStart, SEEK_SET);
  281.     
  282.     if (pwrite->avihdr.dwFlags & AVIF_ISINTERLEAVED) {
  283.         /* Start the 'rec' list */
  284.         pwrite->ckRECORD.cksize = 0;
  285.         pwrite->ckRECORD.fccType = listtypeAVIRECORD;
  286.         if (mmioCreateChunk(pwrite->hmmio, &pwrite->ckRECORD, MMIO_CREATELIST)) {
  287.         goto FileError;
  288.         }
  289.  
  290.         pwrite->dwIndexRec = pwrite->dwIndex;
  291.  
  292.         if (!AddChunkToIndex(pwrite, &pwrite->ckRECORD, AVIIF_LIST))
  293.         return AVIERR_MEMORY;
  294.     }
  295.     }
  296.  
  297.     if (!(dwFlags & AVIIF_NOTIME)) {
  298.     if (pwrite->strhdr[stream].dwSampleSize)
  299.         pwrite->strhdr[stream].dwLength += cbData / pwrite->strhdr[stream].dwSampleSize;
  300.     else
  301.         ++pwrite->strhdr[stream].dwLength;
  302.     }
  303.     
  304. //    ck.ckid = MAKEAVICKID(cktype, stream);
  305.     ck.ckid = ((LONG) cktype << 16) | ('0' << 8) | ('0' + stream);
  306.     ck.cksize = cbData;
  307.  
  308.     if (mmioCreateChunk(pwrite->hmmio, &ck, 0))
  309.     goto FileError;
  310.  
  311.     if (mmioWrite(pwrite->hmmio, lpData, cbData) != (LONG) cbData)
  312.     goto FileError;
  313.  
  314.     if (mmioAscend(pwrite->hmmio, &ck, 0))
  315.     goto FileError;
  316.  
  317.     if (!AddChunkToIndex(pwrite, &ck, dwFlags))
  318.     return AVIERR_MEMORY;
  319.     
  320.     return AVIERR_OK;
  321.     
  322. FileError:
  323.     return AVIERR_FILEWRITE;
  324. }
  325.  
  326. ///////////////////////////////////////////////////////////////////////////
  327. ///////////////////////////////////////////////////////////////////////////
  328.  
  329. /*****************************************************************************
  330.  * @doc EXTERNAL 
  331.  * 
  332.  * @api LONG | avifileEndRecord | Mark the end of a record of data for
  333.  *    interleaved files.
  334.  * 
  335.  * @parm HAVI | hfile | The handle returned from <f avifileOpen>.
  336.  * 
  337.  * @rdesc Returns AVIERR_OK if successful, an error code otherwise.
  338.  *
  339.  * @xref avifileWrite
  340.  ****************************************************************************/
  341. LONG FAR avifileEndRecord(HAVI    hfile)
  342. {
  343.     PPHYS pwrite = (PPHYS) hfile;
  344.  
  345.     if (pwrite->avihdr.dwFlags & AVIF_ISINTERLEAVED) {
  346.     /* !!!! Pad here? */
  347.  
  348.     if (mmioAscend(pwrite->hmmio, &pwrite->ckRECORD, 0))
  349.         goto FileError;
  350.  
  351.     pwrite->hpIndex[pwrite->dwIndexRec].dwChunkLength = pwrite->ckRECORD.cksize;
  352.  
  353.     /* Start the next 'rec' list */
  354.     pwrite->ckRECORD.cksize = 0;
  355.     pwrite->ckRECORD.fccType = listtypeAVIRECORD;
  356.     if (mmioCreateChunk(pwrite->hmmio, &pwrite->ckRECORD, MMIO_CREATELIST)) {
  357.         goto FileError;
  358.     }
  359.  
  360.     pwrite->dwIndexRec = pwrite->dwIndex;
  361.     if (!AddChunkToIndex(pwrite, &pwrite->ckRECORD, AVIIF_LIST))
  362.         return AVIERR_MEMORY;
  363.     }
  364.  
  365.     return AVIERR_OK;
  366.     
  367. FileError:
  368.     return AVIERR_FILEWRITE;
  369. }
  370.  
  371. ///////////////////////////////////////////////////////////////////////////
  372. ///////////////////////////////////////////////////////////////////////////
  373.  
  374. /*****************************************************************************
  375.  * @doc EXTERNAL 
  376.  * 
  377.  * @api LONG | avifileClose | Finish writing and close an AVI file.
  378.  * 
  379.  * @parm HAVI | hfile | The handle returned from <f avifileOpen>.
  380.  * 
  381.  * @rdesc Returns AVIERR_OK if successful, an error code otherwise.
  382.  *
  383.  * @xref avifileOpen
  384.  ****************************************************************************/
  385. LONG FAR avifileClose(HAVI hfile)
  386. {
  387.     PPHYS pwrite = (PPHYS) hfile;
  388.     int                stream;
  389.     MMCKINFO            ck;
  390.     MMCKINFO            ckRIFF;
  391.     MMCKINFO            ckLIST;
  392.     MMCKINFO            ckStream;
  393.     LONG            lCur;
  394.     LONG            lRet = AVIERR_OK;
  395.  
  396.     // Make the main header
  397.     if (pwrite->avihdr.dwMicroSecPerFrame == 0) {
  398.     pwrite->avihdr.dwMicroSecPerFrame =
  399.         muldiv32(1000000L,
  400.              pwrite->strhdr[0].dwScale,
  401.              pwrite->strhdr[0].dwRate);
  402.     }
  403.                        
  404.     pwrite->avihdr.dwFlags |= AVIF_HASINDEX;           
  405.     pwrite->avihdr.dwFlags &= ~(AVIF_ISINTERLEAVED | AVIF_WASCAPTUREFILE |
  406.                     AVIF_MUSTUSEINDEX);
  407.     
  408.     pwrite->avihdr.dwTotalFrames = pwrite->strhdr[0].dwLength;       // !!!
  409.     pwrite->avihdr.dwInitialFrames = 0;              // !!!
  410.     
  411.     // Go back and write out the header
  412.  
  413.     lCur = mmioSeek(pwrite->hmmio, 0, SEEK_CUR);
  414.     mmioSeek(pwrite->hmmio, 0, SEEK_SET);
  415.  
  416.     /* Create RIFF chunk */
  417.     ckRIFF.cksize = 0;
  418.     ckRIFF.fccType = formtypeAVI;
  419.     if (mmioCreateChunk(pwrite->hmmio, &ckRIFF, MMIO_CREATERIFF)) {
  420.     goto FileError;
  421.     }
  422.  
  423.     /* Create header list */
  424.     ckLIST.cksize = 0;
  425.     ckLIST.fccType = listtypeAVIHEADER;
  426.     if (mmioCreateChunk(pwrite->hmmio, &ckLIST, MMIO_CREATELIST)) {
  427.     goto FileError;
  428.     }
  429.  
  430.     /* Create AVI header chunk */
  431.     ck.cksize = sizeof(pwrite->avihdr);
  432.     ck.ckid = ckidAVIMAINHDR;
  433.     if (mmioCreateChunk(pwrite->hmmio, &ck, 0)) {
  434.     goto FileError;
  435.     }
  436.  
  437.     /* Write AVI header info */
  438.     if (mmioWrite(pwrite->hmmio,
  439.           (LPSTR)&pwrite->avihdr,
  440.           sizeof(pwrite->avihdr)) != sizeof(pwrite->avihdr)) {
  441.     goto FileError;
  442.     }
  443.  
  444.     if (mmioAscend(pwrite->hmmio, &ck, 0)) {
  445.     goto FileError;
  446.     }
  447.  
  448.     for (stream = 0; stream < (int) pwrite->avihdr.dwStreams; stream++) {
  449.     /* Create stream header list */
  450.     ckStream.cksize = 0;
  451.     ckStream.fccType = listtypeSTREAMHEADER;
  452.     if (mmioCreateChunk(pwrite->hmmio,&ckStream,MMIO_CREATELIST)) {
  453.         goto FileError;
  454.     }
  455.  
  456.     ck.ckid = ckidSTREAMHEADER;
  457.     if (mmioCreateChunk(pwrite->hmmio, &ck, 0)) {
  458.         goto FileError;
  459.     }
  460.  
  461.     if (mmioWrite(pwrite->hmmio,
  462.               (LPVOID) &pwrite->strhdr[stream],
  463.               sizeof(pwrite->strhdr[stream])) != sizeof(pwrite->strhdr[stream])) {
  464.         goto FileError;
  465.     }
  466.  
  467.     if (mmioAscend(pwrite->hmmio, &ck, 0)) {
  468.         goto FileError;
  469.     }
  470.  
  471.  
  472.     ck.cksize = pwrite->cbFormat[stream];
  473.     ck.ckid = ckidSTREAMFORMAT;
  474.     
  475.     if (mmioCreateChunk(pwrite->hmmio, &ck, 0))
  476.         goto FileError;
  477.  
  478.     if (mmioWrite(pwrite->hmmio, pwrite->lpFormat[stream], ck.cksize) !=
  479.             (LONG) ck.cksize)
  480.         goto FileError;
  481.  
  482.     if (mmioAscend(pwrite->hmmio, &ck, 0))
  483.         goto FileError;
  484.     
  485.     /* Ascend out of stream's header */
  486.     if (mmioAscend(pwrite->hmmio, &ckStream, 0)) {
  487.         goto FileError;
  488.     }
  489.     }
  490.  
  491.         /* ascend from the Header list */
  492.     if (mmioAscend(pwrite->hmmio, &ckLIST, 0)) {
  493.     goto FileError;
  494.     }
  495.  
  496.     /* Pad this header out so that the real data will start on a 2K 
  497.     ** boundary by writing a JUNK chunk.
  498.     */
  499.     ck.ckid = ckidAVIPADDING;
  500.     if (mmioCreateChunk(pwrite->hmmio,&ck,0)) {
  501.     goto FileError;
  502.     }
  503.  
  504.     if (mmioSeek(pwrite->hmmio, 0, SEEK_CUR) >
  505.             (LONG) (pwrite->dwStart - 3 * sizeof(DWORD))) {
  506.     // !!! Ack: we didn't leave enough space for the header.
  507.     // !!! How can we avoid this?
  508.     goto FileError;
  509.     }
  510.     
  511.     mmioSeek(pwrite->hmmio, pwrite->dwStart - 3 * sizeof(DWORD), SEEK_SET);
  512.  
  513.     if (mmioAscend(pwrite->hmmio, &ck, 0)) {
  514.     goto FileError;
  515.     }
  516.  
  517.     /* Start the 'movi' list, where all of the actual data will be. */
  518.     ckLIST.cksize = 0;
  519.     ckLIST.fccType = listtypeAVIMOVIE;
  520.     if (mmioCreateChunk(pwrite->hmmio, &ckLIST, MMIO_CREATELIST)) {
  521.     goto FileError;
  522.     }
  523.  
  524.     mmioSeek(pwrite->hmmio, lCur, SEEK_SET);
  525.  
  526.     if (mmioAscend(pwrite->hmmio, &ckLIST, 0))
  527.     goto FileError;
  528.  
  529.     /*
  530.     ** Now write index out!
  531.     */
  532.     if (!WriteIndex(pwrite))
  533.     goto FileError;
  534.  
  535. FinishUp:
  536.     if (mmioAscend(pwrite->hmmio, &ckRIFF, 0))
  537.     goto FileError;
  538.  
  539.     if (mmioFlush(pwrite->hmmio, 0))
  540.     goto FileError;
  541.     
  542.     /* Close the file */
  543.     if (mmioClose(pwrite->hmmio, 0))
  544.     goto FileError;
  545.  
  546.  
  547.     GlobalFreePtr(pwrite);  // C6 doesn't like this line
  548.     
  549.     return lRet;
  550.     
  551. FileError:
  552.     lRet = AVIERR_FILEWRITE;
  553.     goto FinishUp;
  554. }
  555. ///////////////////////////////////////////////////////////////////////////
  556. ///////////////////////////////////////////////////////////////////////////
  557.  
  558. /* Internal indexing functions.... */
  559.  
  560. #define INDEXALLOC    256
  561.  
  562. static BOOL NEAR PASCAL InitIndex(PPHYS pphys)
  563. {
  564.     pphys->hpIndex = (AVIINDEXENTRY huge *)
  565.         GlobalAllocPtr(GMEM_MOVEABLE, 
  566.             INDEXALLOC * sizeof(AVIINDEXENTRY));
  567.     if (!pphys->hpIndex)
  568.     return FALSE;
  569.     pphys->dwIndex = 0;
  570.     pphys->dwIndexAlloc = INDEXALLOC;
  571.     return TRUE;
  572. }
  573.  
  574. static BOOL NEAR PASCAL AddChunkToIndex(PPHYS pphys, MMCKINFO FAR *lpck, DWORD dwFlags)
  575. {
  576.     if (pphys->dwIndex == pphys->dwIndexAlloc) {
  577.     AVIINDEXENTRY huge * hp;
  578.     
  579.     hp = (AVIINDEXENTRY huge *)
  580.         GlobalReAllocPtr(pphys->hpIndex, 
  581.         (pphys->dwIndexAlloc + INDEXALLOC) * sizeof(AVIINDEXENTRY), 
  582.         GMEM_MOVEABLE);
  583.     if (!hp)
  584.         return FALSE;
  585.     
  586.     pphys->hpIndex = hp;
  587.     pphys->dwIndexAlloc += INDEXALLOC;
  588.     }
  589.     
  590.     /* Record the position of the chunk we just wrote out. */
  591.     pphys->hpIndex[pphys->dwIndex].ckid = lpck->ckid;
  592.     pphys->hpIndex[pphys->dwIndex].dwChunkLength = lpck->cksize;
  593.     /* dwChunkOffset is the offset of the chunk itself, not the
  594.     ** data contained in the chunk....
  595.     */
  596.     // !!! fix to write out relative indexes!
  597.     pphys->hpIndex[pphys->dwIndex].dwChunkOffset = 
  598.                 lpck->dwDataOffset - 2 * sizeof(DWORD);
  599.     pphys->hpIndex[pphys->dwIndex].dwFlags = dwFlags;
  600.     pphys->dwIndex++;
  601.     
  602.     return TRUE;
  603. }
  604.  
  605. static BOOL NEAR PASCAL WriteIndex(PPHYS pphys)
  606. {
  607.     MMCKINFO ck;
  608.     
  609.     ck.ckid = ckidAVINEWINDEX;
  610.     ck.cksize = sizeof(AVIINDEXENTRY) * pphys->dwIndex;
  611.     
  612.     if (mmioCreateChunk(pphys->hmmio, &ck, 0))
  613.     return FALSE;
  614.  
  615.     if (mmioWrite(pphys->hmmio, (HPSTR) pphys->hpIndex, ck.cksize) != 
  616.                 (LONG) ck.cksize)
  617.     return FALSE;
  618.  
  619.     if (mmioAscend(pphys->hmmio, &ck, 0))
  620.     return FALSE;
  621.    
  622.     return TRUE;
  623. }
  624.  
  625.  
  626.  
  627.  
  628. /*****************************************************************************
  629.  * @doc EXTERNAL 
  630.  * 
  631.  * @api LONG | aviVideoOpen | Helper function for writing AVI files consisting
  632.  *    only of a single video stream.
  633.  * 
  634.  * @parm HAVI FAR * | lphfile | Holds the returned file handle if the
  635.  *    function succeeds.
  636.  * 
  637.  * @parm LPSTR | lpFileName | The file name to use.
  638.  *
  639.  * @parm LPBITMAPINFOHEADER | lpbi | The format of the video to be written.
  640.  *
  641.  * @parm DWORD | dwMicroSecPerFrame | The spacing of frames in time.  If
  642.  *    zero, a default of 15 frames/sec will be used.
  643.  *
  644.  * @rdesc Returns AVIERR_OK if successful, an error code otherwise.
  645.  *
  646.  * @xref avifileOpen
  647.  ****************************************************************************/
  648. LONG FAR aviVideoOpen(HAVI FAR *lphfile,
  649.               LPSTR lpFileName,
  650.               LPBITMAPINFOHEADER lpbi,
  651.               DWORD dwMicroSecPerFrame)
  652. {
  653.     LONG    l;
  654.     MainAVIHeader   hdrNew;
  655.     AVIStreamHeader strhdr;
  656.  
  657.     if (dwMicroSecPerFrame == 0)
  658.     dwMicroSecPerFrame = 1000000L/15;
  659.  
  660.     _fmemset(&hdrNew, 0, sizeof(hdrNew));
  661.     hdrNew.dwMicroSecPerFrame = dwMicroSecPerFrame;
  662.     hdrNew.dwMaxBytesPerSec = 0;      
  663.     hdrNew.dwPaddingGranularity = 0;  
  664.                        
  665.     hdrNew.dwFlags = AVIF_HASINDEX;           
  666.     hdrNew.dwTotalFrames = 0;
  667.     
  668.     hdrNew.dwStreams = 1;           
  669.     hdrNew.dwSuggestedBufferSize = 0;
  670.                
  671.     hdrNew.dwWidth = lpbi->biWidth;
  672.     hdrNew.dwHeight = lpbi->biHeight;
  673.     
  674.     l = avifileOpen(lphfile, lpFileName, &hdrNew);
  675.  
  676.     if (l != AVIERR_OK)
  677.     return l;
  678.  
  679.     _fmemset(&strhdr, 0, sizeof(strhdr));
  680.     strhdr.fccType                = streamtypeVIDEO;
  681.     strhdr.fccHandler             = 0;
  682.     strhdr.dwScale                = dwMicroSecPerFrame;
  683.     strhdr.dwRate                 = 1000000;
  684.     strhdr.dwSuggestedBufferSize  = lpbi->biSizeImage;
  685.  
  686.     l = avifileAddStream(*lphfile, NULL, &strhdr, lpbi,
  687.              lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD));
  688.     
  689.     if (l != AVIERR_OK)
  690.     avifileClose(*lphfile);
  691.  
  692.  
  693.     return l;
  694. }
  695.     
  696.  
  697. /*****************************************************************************
  698.  * @doc EXTERNAL 
  699.  * 
  700.  * @api LONG | avifileWrite | Write data to an AVI file.
  701.  * 
  702.  * @parm HAVI | hfile | The handle returned from <f avifileOpen>.
  703.  * 
  704.  * @parm LPBITMAPINFOHEADER | lpbi | The format of the frame to write.
  705.  *
  706.  * @parm LPVOID | lpData | A pointer to the bits to write.
  707.  *
  708.  * @parm DWORD | dwFlags | Flags associated with this data.  In particular:
  709.  *
  710.  * @flag AVIIF_KEYFRAME | This data represents a key frame.
  711.  *
  712.  * @rdesc Returns AVIERR_OK if successful, an error code otherwise.
  713.  *
  714.  * @xref avifileWrite
  715.  ****************************************************************************/
  716. LONG FAR aviVideoWriteFrame(HAVI hfile, LPBITMAPINFOHEADER lpbi, LPVOID lp, DWORD dwFlags)
  717. {
  718.     if (lpbi == NULL)
  719.     return -1;
  720.     
  721.     if (lp == NULL)
  722.     lp = (LPBYTE) lpbi + lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
  723.     
  724.     return avifileWrite(hfile, 0, lp, lpbi->biSizeImage,
  725.          (lpbi->biCompression ? cktypeDIBcompressed : cktypeDIBbits),
  726.          dwFlags);
  727. }
  728.